use crate::AppState;
use axum::{extract::State, http::StatusCode, response::IntoResponse, Json};
use chat_core::{
    error::{AppError, ErrorOutput},
    CreateUser, SigninUser, SignupUser,
};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

#[derive(Debug, Serialize, ToSchema, Deserialize)]
pub struct AuthOutput {
    pub token: String,
}

#[utoipa::path(
    post,
    path = "/api/signup",
    request_body = SignupUser,
    responses(
        (status = 201, description = "Created",content_type="application/json", body = AuthOutput),
        (status = 403, description = "Forbidden", body = ErrorOutput),
    ),
)]
pub(crate) async fn signup_handler(
    State(state): State<AppState>,
    Json(signup_user): Json<SignupUser>,
) -> Result<impl IntoResponse, AppError> {
    //  judge ws if exists
    let ws = state.create_ws(&signup_user.ws_name).await?;
    let create_user = CreateUser::new(
        signup_user.fullname,
        signup_user.email,
        ws.id,
        signup_user.password,
    );
    let user = state.create_user(&create_user).await?;
    state
        .update_ws_owner_by_id(ws.id, user.id, &user.fullname)
        .await?;
    let token = state.ek.sign(user.clone())?;
    Ok((StatusCode::CREATED, Json(AuthOutput { token })))
}

#[utoipa::path(
  post,
  path = "/api/signin",
  request_body = SignupUser,
  responses(
      (status = 201, description = "User signed in",content_type="application/json", body = AuthOutput),
      (status = 403, description = "Forbidden", body = ErrorOutput),
  ),
)]
pub(crate) async fn signin_handler(
    State(state): State<AppState>,
    Json(sigin_user): Json<SigninUser>,
) -> Result<impl IntoResponse, AppError> {
    // 获取连接池
    let login_user = state.verify_user_token(&sigin_user).await?;
    match login_user {
        Some(login_user) => {
            let token = state.ek.sign(login_user)?;
            let ao = AuthOutput { token };
            Ok((StatusCode::OK, Json(ao)).into_response())
        }
        None => Ok((
            StatusCode::FORBIDDEN,
            Json(ErrorOutput::new("Invalid email or password")),
        )
            .into_response()),
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use anyhow::Result;
    use http_body_util::BodyExt;
    use jwt_simple::reexports::rand::{self, Rng};

    #[tokio::test]
    async fn signup_should_work() -> Result<()> {
        let state = AppState::new_for_test().await?;
        let mut rng = rand::thread_rng();
        let random_number: u8 = rng.gen();
        let email = format!("jinyulinlang{0}@qq.com", random_number).to_string();
        let fullname = format!("jinyulinlang{0}", random_number);
        let password = "123456".to_string();
        let ws_name = "jinyulinlang".to_string();
        let signup_user = SignupUser::new(fullname, email.clone(), ws_name.clone(), password);
        let ret = signup_handler(State(state.clone()), Json(signup_user))
            .await?
            .into_response();
        let bytes = ret.into_body().collect().await?.to_bytes();
        let ret: AuthOutput = serde_json::from_slice(&bytes)?;
        assert_ne!(ret.token, "");
        sqlx::query(" delete from   users where email = $1")
            .bind(&email)
            .execute(&state.pool)
            .await?;
        state.del_ws_by_name(&ws_name).await?;
        Ok(())
    }
    #[tokio::test]
    async fn signin_should_work() -> Result<()> {
        let state = AppState::new_for_test().await?;
        let email = "jinyulinlang@qq.com".to_string();
        sqlx::query(" delete from   users  where email = $1;")
            .bind(&email)
            .execute(&state.pool)
            .await?;
        // 创建一个用户
        let create_user = CreateUser::new(
            "jinyulinlang".to_string(),
            email.clone(),
            0,
            "123456".to_string(),
        );
        // 创建用户
        let _user = state.create_user(&create_user).await?;
        // 登录用户
        let signin_user = SigninUser::new("jinyulinlang@qq.com".to_string(), "123456".to_string());
        let ret = signin_handler(State(state.clone()), Json(signin_user))
            .await?
            .into_response();
        //  断言登录成功
        assert_eq!(ret.status(), StatusCode::OK);
        let bytes = ret.into_body().collect().await?.to_bytes();
        let signin_ao: AuthOutput = serde_json::from_slice(&bytes)?;
        // 断言token不为空
        assert_ne!(signin_ao.token, "");
        sqlx::query(" delete from   users  where email = $1;")
            .bind(&email)
            .execute(&state.pool)
            .await?;
        Ok(())
    }
}
